﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;

namespace MadLibrary.Collections
{
    /// <summary>
    /// Contains several extension methods for collections.
    /// </summary>
    public static class CollectionUtilities
    {
        private static readonly Random random;
        static CollectionUtilities()
        {
            random = new Random();
        }

        /// <summary>
        /// Maps the specified <see cref="System.Action"/> to each one of array items.
        /// </summary>
        /// <param name="this">The array to map.</param>
        /// <param name="delegate">The action to perform.</param>
        public static void Map<T> (this IEnumerable<T> @this, Action<T> @delegate)
        {
            foreach (T t in @this)
                @delegate(t);
        }
        /// <summary>
        /// Takes a collection and returns a shuffeld array with the collection's elements.
        /// </summary>
        /// <typeparam name="T">The type of the elements.</typeparam>
        /// <param name="this">The collection to shuffle.</param>
        /// <returns>A <see cref="System.Array"/>.</returns>
        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> @this)
        {
            return @this.OrderBy(
                (v) => 
                { 
                    return random.Next(); 
                });
        }

        /// <summary>
        /// Checks if the source value is equal to one of the items in args.
        /// </summary>
        /// <typeparam name="T">A generic type that is the type of the source value.</typeparam>
        /// <typeparam name="S">The type of the values in args. Must be equal to T or a base class of T.</typeparam>
        /// <param name="this">The source value.</param>
        /// <param name="args">The destination values.</param>
        /// <returns>True if @this is in args, false otherwise.</returns>
        public static bool IsIn<T, S>(this T @this, params S[] args) where T:S
        {
            if (args == null)
                throw new ArgumentNullException();
            return args.Contains(@this);
        }

        /// <summary>
        /// Compresses the given boolean array into a 64 bit integer.
        /// A compressed array is 8 times smaller than the original array.
        /// </summary>
        /// <param name="this">The array to compress.</param>
        /// <returns>A <see cref="System.Numerics.BigInteger"/> containing the values of the array.</returns>
        public static BigInteger Compress(this bool[] @this)
        {
            if (@this == null)
                throw new ArgumentNullException();
            BigInteger result = 0;
            foreach (bool b in @this.Reverse())
            {
                result <<= 1;
                if (b)
                    result++;
                
            }
            return result;
        }
        /// <summary>
        /// Decompress a number and its bits into an array of booleans.
        /// </summary>
        /// <param name="this">The array to decompress to.</param>
        /// <param name="values">The values to decompress.</param>
        /// <param name="startIndex">The start index in which the writing starts.</param>
        public static void Decompress(this bool[] @this, BigInteger values, int startIndex)
        {
            if (@this.Length - startIndex < 64)
                throw new ArgumentOutOfRangeException("Array is too small to contain all values or not enough space is given.");
            if (@this == null)
                throw new ArgumentNullException();

            int i = startIndex;
            BigInteger value = values;
            for (int j = 0; j < 64; j++, value >>= 1)
                @this[i++] = (value & 1) == 1;

        }
    }
}
